/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2021年8月28日
 */
package com.massyframework.beanskin.runtime.nativelib;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutorService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.massyframework.beanskin.Module;
import com.massyframework.beanskin.ModuleCreatedEvent;
import com.massyframework.beanskin.ModuleDestoryingEvent;
import com.massyframework.beanskin.ModuleEventAdapter;
import com.massyframework.beanskin.ModuleException;
import com.massyframework.beanskin.ModuleIdentifier;
import com.massyframework.beanskin.ModuleRepository;
import com.massyframework.beanskin.SimpleModuleIdentifier;
import com.massyframework.beanskin.assembly.Assembly;
import com.massyframework.beanskin.customer.Customer;
import com.massyframework.beanskin.framework.Framework;
import com.massyframework.beanskin.framework.FrameworkDestroyingEvent;
import com.massyframework.beanskin.framework.FrameworkEvent;
import com.massyframework.beanskin.framework.FrameworkInitializingEvent;
import com.massyframework.beanskin.framework.FrameworkListener;
import com.massyframework.beanskin.framework.FrameworkStartedEvent;
import com.massyframework.beanskin.license.License;
import com.massyframework.beanskin.license.LicenseBindedEvent;
import com.massyframework.beanskin.license.LicenseChangedEvent;
import com.massyframework.beanskin.license.LicenseUnbindedEvent;
import com.massyframework.beanskin.runtime.ModuleRegistry;
import com.massyframework.beanskin.service.ServiceRepository;
import com.massyframework.beanskin.service.secure.VerifySignFailedException;
import com.massyframework.beanskin.util.IOUtils;
import com.massyframework.beanskin.util.classloading.ModuleClassLoader;
import com.massyframework.beanskin.util.exceptioncollect.ExceptionCollectUtils;
import com.massyframework.beanskin.util.localstore.LocalStore;

/**
 * 模块监视器
 * <p>
 * 监听模块安装，卸载事件
 * @author huangkh
 *
 */
public class ModuleMonitor extends ModuleEventAdapter
	implements FrameworkListener {

	private Logger logger = LoggerFactory.getLogger(ModuleMonitor.class);
	
	private NativeLibrary library;
	private LocalStore localStore;
	private String subPath;
	private Customer customer;
	
	private volatile boolean started = false;
	private ModuleRegistry registry;
	private ExecutorService executor;
	
	/**
	 * 构造方法
	 * @param library {@link NativeLibrary}, 原生动态库
	 * @param localStore {@link localStore}, 本地存储
	 * @param customerId {@link int},客户编号
	 */
	public ModuleMonitor(NativeLibrary library, LocalStore localStore, Customer customer) {
		this.library = Objects.requireNonNull(library, "\"library\" cannot be null.");
		this.localStore = Objects.requireNonNull(localStore, "\"localStore\" cannot be null.");
		this.customer = customer;
		this.subPath = "license".concat(File.separator).concat("licenses.xml");
	}
	
	@Override
	public void onEvent(FrameworkEvent event) {
		if (event instanceof FrameworkInitializingEvent) {
			Framework framework = event.getFramework();
			
			//输入logo
			System.out.println(this.loadLogo().toString());
			long id = customer == null ? 0 : customer.getId();
			String name = customer == null ? null : customer.getName();
			String signature = customer == null ? null : customer.getSignature();
			
			if (!this.frameworkInitializing(framework, id, name, signature, library.getLibPath(), this.loadJarsignData())) {
				ModuleException exception = 
						new VerifySignFailedException(SimpleModuleIdentifier.createModuleIdentifierWithKernel());
				if (logger.isErrorEnabled()) {
					logger.error(exception.getMessage(), exception);
				}
				ExceptionCollectUtils.addException(exception);
			}
			return;
		}
		
		if (event instanceof FrameworkStartedEvent) {
			this.onFrameworkStarted(event.getFramework());
			return;
		}
		
		if (event instanceof FrameworkDestroyingEvent) {
			Framework framework = event.getFramework();
			this.frameworkDestroying(framework);
			this.library.unload();
			return;
		}
	}
	
	/**
	 * 加载签名数据
	 * @return {@link String}
	 */
	protected String loadJarsignData() {
		ModuleIdentifier identifier =
				SimpleModuleIdentifier.createModuleIdentifierWithKernel();
		String subPath = "extends".concat(File.separator).concat("jarsign.data");
		File file = 
				this.localStore.getDeploymentDirectory(identifier)
					.resolve(subPath)
					.toFile();
		
		String result = null;
		BufferedReader br = null;
		if (file.exists()) {
			StringBuilder builder = new StringBuilder();
			InputStream is = null;
			try {
				is = new FileInputStream(file);
				
				br = new BufferedReader(new InputStreamReader(is));
		        String line;
		        while ((line = br.readLine()) != null) {
	                builder.append(line);
	            }
	            result = builder.toString();
			}catch(IOException e) {
				ExceptionCollectUtils.addException(e);
				return null;
			}finally {
				IOUtils.close(is);
			}
		}
		
		return result;
	}
	
	/**
     * 加载图标
     * @return {@link StringBuilder}
     */
    protected StringBuilder loadLogo(){
        StringBuilder result = new StringBuilder();
        URL url = this.getClass().getClassLoader().getResource("BEANSKIN-INF/enterprise.logo");
        if (url != null){
            BufferedReader reader = null;
            String line = null;
            boolean top = true;
            try{
                reader = new BufferedReader(new InputStreamReader(url.openStream()));
                while ((line = reader.readLine()) != null){
                    if (!top){
                        result.append("\r\n");
                    }
                    result.append(line);
                    if (top){
                        top = false;
                    }
                }
            }catch(Exception e){

            }finally {
                IOUtils.close(reader);
            }
        }

        return result;
    }
    
	@Override
	protected void onCreated(ModuleCreatedEvent event) {
		Module<?> module = event.getModule();
		ModuleClassLoader loader = module.getModuleClassLoader();
		if (loader.getPublicKey() != null) {
			this.moduleInstalled(
					module.toIdentifier(), 
					loader.getPublicKey(), 
					loader.getX509Name(), 
					loader.getX509Encrypted(), 
					module);
		}
		
		if (this.started) {
			List<Module<?>> modules = new ArrayList<>();
			modules.add(event.getModule());
			this.executor.execute(new Task(modules));
		}		
	}

	/**
	 * 在模块中扫描许可
	 * @param module {@link Module}
	 */
	protected void scanLicenseWithModule(Module<?> module) {
		File xmlFile = this.localStore.getDeploymentDirectory(module).resolve(this.subPath).toFile();
		if (xmlFile.exists()) {	
			LicensesXmlParser parser = new LicensesXmlParser();
			List<License> licenses = null;
			try {
				licenses = parser.parseFrom(xmlFile);							
				for (License license: licenses) {					
					if (license.getCustomerId() == 0) {
						registry.bindLicense(license);
					}else {
						if (license.getCustomerId() == this.customer.getId()) {
							registry.bindLicense(license);
						}
					}
				}
			}catch(Exception e) {
				if (logger.isErrorEnabled()) {
					logger.error("parse license.xml failed:".concat(module.toIdentifier()), e);
				}
			}			
		}	
	}
	
	@Override
	protected void onDestorying(ModuleDestoryingEvent event) {
		Module<?> module = event.getModule();
		//模块卸载时，无需关心许可卸载事宜。
		ModuleClassLoader loader = module.getModuleClassLoader();
		if (loader.getPublicKey() != null) {
			this.moduleUninstalling(module.toIdentifier(), module);
		}
	}
	
	@Override
	protected void onLicenseBindedEvent(LicenseBindedEvent event) {
		Module<?> module = event.getModule();
		License license = module.getLicense();
		this.licenseBinded(module.toIdentifier(), license);
	}

	@Override
	protected void onLicenseUnbindedEvent(LicenseUnbindedEvent event) {
		Module<?> module = event.getModule();
		this.licenseUnbinded(module.toIdentifier());
	}

	@Override
	protected void onLicenseChangedEvent(LicenseChangedEvent event) {
		Module<?> module = event.getModule();
		License license = module.getLicense();
		this.licenseChanged(module.toIdentifier(), license);
	}

	/**
	 * framework初始化
	 * @param framework {@link Framework}
	 * @param customerId {@link long},客户编号
	 * @param customerName {@link String},客户名称, 可能为null.
	 * @param signature {@link String},签名,可能为null. 
	 * @param libPath 加载动态库路径
	 * @param jarsignData jar签名数据，可能为null.
	 * @return {@link boolean}, 验证jar签名失败则返回{@link false}
	 */
	protected native boolean frameworkInitializing(Framework framework, 
			long customerId, String customerName, String signature,
			String libPath, String jarsignData);
	
	/**
	 * 运行框架已启动完成事件
	 * @param framework {@link Framework}
	 */
	protected synchronized void onFrameworkStarted(Framework framework) {
		Assembly kernel = (Assembly)framework.findModuleById(0);
		ServiceRepository repo = ServiceRepository.retrieveFrom(kernel);
		this.registry = (ModuleRegistry) repo.getService(ModuleRepository.class);
		this.executor = repo.getService(ExecutorService.class, "(service.name=frameworkThread)");
		this.started = true;
		
		List<Module<?>> modules = framework.getModules();
		this.executor.execute(new Task(modules));
	}
	
	/**
	 * framework销毁
	 * @param framework
	 */
	private native void frameworkDestroying(Framework framework);
	
	/**
	 * 需要解密处理的模块安装
	 * @param identifier {@link String},模块标示符
	 * @param publicKey {@link String},公钥
	 * @param certificationName {@link String},证书名称
	 * @param certificationData {@link String},证书数据
	 * @param module {@link Module},模块
	 */
	private native void moduleInstalled(String identifier, String publicKey, String certificationName, String certificationData, Module<?> module);
	
	/**
	 * 需要解密处理的模块卸载
	 * @param identifier {@link String},模块标示符
	 * @param module {@link Module},模块
	 */
	private native void moduleUninstalling(String identifier, Module<?> module);
	
	/**
	 * 许可绑定
	 * @param identifier {@link String},模块标示符
	 * @param license {@link License},许可
	 */
	private native void licenseBinded(String identifier, License license);
	
	/**
	 * 更改许可
	 * @param identifier {@link String},模块标示符
	 * @param license {@link License},许可
	 */
	private native void licenseChanged(String identifier, License license);
	
	/**
	 * 许可取消绑定
	 * @param identifier {@link String},模块标示符
	 */
	private native void licenseUnbinded(String identifier);
	
	/**
	 * 任务
	 * @author huangkh
	 */
	private class Task implements Runnable {
		
		private List<Module<?>> modules;
		
		public Task(List<Module<?>> modules) {
			this.modules = modules;
		}

		@Override
		public void run() {
			if (!modules.isEmpty()) {			
				for (Module<?> module: modules) {
					if (module.getId() != 0) {
						scanLicenseWithModule(module);
					}
				}
				
			}
			
		}
		
		
	}
}
